/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.redis.manager;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.iwindplus.boot.domain.constant.CommonConstant;
import com.iwindplus.boot.domain.enums.BizCodeEnum;
import com.iwindplus.boot.domain.exception.BizException;
import com.iwindplus.boot.redis.domain.constant.RedisConstant;
import com.iwindplus.boot.redis.domain.enums.RedissonLockType;
import jakarta.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;

/**
 * Redis生成流水号管理器（分布式锁方式）.
 *
 * @author zengdegui
 * @since 2018/9/5
 */
@Slf4j
public class SerialNumManager {

    @Resource
    private RedissonManager redissonManager;

    /**
     * 生成流水号.
     *
     * @return String
     */
    public String getSerialNum() {
        return this.getSerialNum(null);
    }

    /**
     * 生成流水号.
     *
     * @param key 键（可选，默认：6位年月日）
     * @return String
     */
    public String getSerialNum(String key) {
        return this.getSerialNum(key, Boolean.TRUE);
    }

    /**
     * 生成流水号.
     *
     * @param key        键（可选，默认：6位年月日）
     * @param dailyReset 是否每天重置（每天序号从新开始）
     * @return String
     */
    public String getSerialNum(String key, Boolean dailyReset) {
        return this.generate(key, CommonConstant.DateConstant.YYMMDD, CommonConstant.NumberConstant.NUMBER_SIX, dailyReset);
    }

    /**
     * 生成指定格式的序列号.
     *
     * @param businessKey 业务标识（如 order、payment）
     * @param datePattern 日期格式（如 "yyMMdd", "yyyyMMdd"）
     * @param length      补零长度（默认 6 位）
     * @param dailyReset  是否每天重置序号（默认 true）
     * @return String 序列号
     */
    public String generate(String businessKey, String datePattern, Integer length, Boolean dailyReset) {
        datePattern = Optional.ofNullable(datePattern).orElse(CommonConstant.DateConstant.YYMMDD);
        length = Optional.ofNullable(length).orElse(CommonConstant.NumberConstant.NUMBER_SIX);
        dailyReset = Optional.ofNullable(dailyReset).orElse(Boolean.TRUE);

        // 构建最终 key
        String today = DateUtil.format(LocalDateTime.now(), datePattern);
        String effectiveBusinessKey = CharSequenceUtil.blankToDefault(businessKey, "default");
        String incKey = String.format("serialNum:%s:%s", effectiveBusinessKey, today);
        String lockKey = RedisConstant.LOCK_KEY_PREFIX.concat(incKey);

        RLock lock = null;
        try {
            lock = redissonManager.getLock(RedissonLockType.LOCK, lockKey);
            lock.lock();

            long ttl = this.getSecondsUntilNextDay() + 1000;
            long value = Boolean.TRUE.equals(dailyReset) ? redissonManager.incrementAndGetWithExpire(incKey, Duration.ofSeconds(ttl)) :
                redissonManager.incrementAndGet(incKey);
            return this.getCompletionSerialNum(today, value, length);
        } catch (Exception ex) {
            log.error("生成序列号失败，businessKey={}", businessKey, ex);
            throw new BizException(BizCodeEnum.GENERATE_SERIAL_NUM_ERROR, new Object[]{lockKey});
        } finally {
            redissonManager.unlock(lock);
        }
    }

    /**
     * 将数值拼接成指定长度的字符串（补零）.
     *
     * @param prefix 前缀（如日期）
     * @param nowNum 当前递增值
     * @param length 补零长度
     * @return String 完整的序列号
     */
    private String getCompletionSerialNum(String prefix, long nowNum, int length) {
        StringBuilder sb = new StringBuilder(prefix);
        String format = String.format("%0" + length + "d", nowNum);
        sb.append(format);
        return sb.toString();
    }

    /**
     * 获取当天剩余秒数，用于设置 TTL.
     *
     * @return long
     */
    private long getSecondsUntilNextDay() {
        Date now = new Date();
        Date endOfDay = DateUtil.endOfDay(now);
        return DateUtil.between(now, endOfDay, DateUnit.SECOND);
    }

}